package com.yvan.serverless.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yvan.platform.JsonWapper;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

import java.util.List;
import java.util.Map;

public class ScriptUtils {

    public static Value require(Context cxt, String path) {
        return cxt.eval("js", "require('" + path + "')");
    }

    /**
     * 根据实体类的构造函数，构造 meta 元数据
     */
    public static Map<String, ?> getEntityConstructMetas(Value entityConstruct) {
        JsonWapper source = new JsonWapper(ScriptUtils.toMap(entityConstruct.getMember("prototype").getMember("__cols")));
        JsonWapper ret = new JsonWapper();
        for (String field : source.keys()) {
            ret.set(field, "type", source.get(field, "type"));
            ret.set(field, "option", source.get(field, "option"));
        }
        return ret.getInnerMap();
    }

    /**
     * 将 js 类型转换为 map
     */
    public static Map<String, Object> toMap(Object valueRaw) {
        Map<String, Object> target = Maps.newLinkedHashMap();

        if ((valueRaw instanceof Value) && ((Value) valueRaw).isHostObject()) {
            valueRaw = ((Value) valueRaw).asHostObject();
        }

        Map<String, Object> source;
        if (valueRaw instanceof Value) {
            // js 类型
            Value value = (Value) valueRaw;
            if (value.hasMembers()) {
                source = Maps.newLinkedHashMap();
                for (String key : value.getMemberKeys()) {
                    // 一个个枚举结果（防止深度转换）
                    source.put(key, value.getMember(key));
                }

            } else {
                // 不是脚本类型, 也不是 map 类型，不用转换
                return target;
            }

        } else if (valueRaw instanceof Map) {
            // map 类型
            source = (Map) valueRaw;

        } else {
            // 不是脚本类型, 也不是 map 类型，不用转换
            return target;

        }

        // 对象或数组
        for (Map.Entry entry : source.entrySet()) {
            if (entry.getKey() == null) {
                continue;
            }
            target.put(entry.getKey().toString(), toValue(entry.getValue()));
        }

        return target;
    }

    /**
     * 将 js 类型转换为 list
     */
    public static List<Object> toList(Object valueRaw) {
        List<Object> target = Lists.newArrayList();

        if ((valueRaw instanceof Value) && ((Value) valueRaw).isHostObject()) {
            valueRaw = ((Value) valueRaw).asHostObject();
        }

        if (valueRaw instanceof Value) {
            // js 类型
            Value value = (Value) valueRaw;
            if (value.hasArrayElements()) {
                long count = value.getArraySize();
                for (long i = 0; i < count; i++) {
                    target.add(value.getArrayElement(i));
                }

            } else {
                // 不是数组类型，不用转换
                return target;
            }

        } else if (valueRaw instanceof List) {
            // map 类型
            target = (List) valueRaw;

        } else {
            // 不是脚本类型, 也不是 map 类型，不用转换
            return target;

        }

        // 对象或数组
        for (int i = 0; i < target.size(); i++) {
            target.set(i, toValue(target.get(i)));
        }
        return target;
    }

    /**
     * 将 js 类型转换为最合适的 java 类型
     */
    public static Object toValue(Object valueRaw) {
        if (valueRaw == null) {
            return null;
        }

        if ((valueRaw instanceof Value) && ((Value) valueRaw).isHostObject()) {
            valueRaw = ((Value) valueRaw).asHostObject();
        }

        if (valueRaw instanceof Map) {
            // 是 Map 类型
            return toMap(valueRaw);
        }

        if (valueRaw instanceof List) {
            // 是 List 类型
            return toList(valueRaw);
        }

        if (!(valueRaw instanceof Value)) {
            // 不是脚本类型，不用转换
            return valueRaw;
        }

        Value value = (Value) valueRaw;
        if (value.isDate()) {
            return value.asDate();

        } else if (value.isTime()) {
            return value.asTime();

        } else if (value.isNull()) {
            return null;
        }

        if (value.canExecute()) {
            // 函数在这里, 顺序不要调
            return null;
        }

        if (value.hasArrayElements()) {
            // 数组在这里, 顺序不要调
            return toList(value);
        }

        Object o = value.as(Object.class);
        if (o instanceof Map) {
            // 对象 / 数组 / 函数 都在这个判断里
            return toMap(valueRaw);
        }

        // 其他 int / double / string / boolean 都可以正常进行
        return o;
    }

}
